package com.fast.alden.admin.security.service.impl;

import com.fast.alden.admin.security.model.UserDetailsImpl;
import com.fast.alden.admin.security.service.DynamicAuthorizationService;
import com.fast.alden.admin.service.SysApiResourceService;
import com.fast.alden.data.model.SysApiResource;
import com.fast.alden.data.model.SysApiResourceRole;
import com.fast.alden.data.model.SysRole;
import com.fast.alden.data.model.SysUser;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.stereotype.Service;
import org.springframework.util.AntPathMatcher;

import java.util.List;
import java.util.function.Supplier;

@Service
public class DynamicAuthorizationServiceImpl implements DynamicAuthorizationService {
    private final AntPathMatcher antPathMatcher = new AntPathMatcher();
    @Resource
    private SysApiResourceService apiResourceService;

    private final AuthorizationStrategy authorizationStrategy;

    public DynamicAuthorizationServiceImpl() {
        authorizationStrategy = new ApiResourceBasedAuthorizationStrategy();
    }

    @Override
    public boolean check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
        return authorizationStrategy.check(authentication, context);
    }

    interface AuthorizationStrategy {
        boolean check(Supplier<Authentication> authentication, RequestAuthorizationContext context);
    }

    /**
     * 基于用户角色的鉴权策略：
     * 根据当前用户拥有的角色，筛选出所有当前用户可访问的API资源，与当前请求匹配
     */
    class UserRoleBasedAuthorizationStrategy implements AuthorizationStrategy {

        @Override
        public boolean check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
            // 获取当前请求和url
            HttpServletRequest request = context.getRequest();
            String url = request.getRequestURI();
            String method = request.getMethod();

            // 如果未登录
            if (authentication.get() instanceof AnonymousAuthenticationToken) {
                List<SysApiResource> apiResources = apiResourceService.list();
                // 找出所有不需要登录即可访问的API资源列表
                List<SysApiResource> allowedList = apiResources.stream().filter(resource -> !resource.getRequireLogin()).toList();

                // 与当前请求进行匹配，匹配成功则放行
                return allowedList.stream().anyMatch(resource ->
                        antPathMatcher.match(resource.getUrl(), url)
                                && (method.equals(resource.getMethod()) || resource.getMethod() == null)
                );
            }

            // 获取当前登录用户以及其角色ID列表
            UserDetailsImpl userDetails = (UserDetailsImpl) authentication.get().getPrincipal();
            SysUser current = userDetails.getUser();
            List<Long> userRoleIds = userDetails.getRoles().stream().map(SysRole::getId).toList();

            // 超级管理员直接返回true
            if (current.getType() == 0) {
                return true;
            }

            List<SysApiResource> apiResources = apiResourceService.list();
            // 找出当前用户所拥有角色可访问的所有请求
            List<SysApiResourceRole> resourceRoleList = apiResourceService.findApiResourceRolesByRoleIdIn(userRoleIds);
            List<SysApiResource> allowedList = apiResources.stream().filter(
                    resource ->
                            // 不需要登录即可访问的API资源
                            !resource.getRequireLogin()
                                    // 仅需要登录即可访问的API资源
                                    || !resource.getRequirePermissions()
                                    // 当前用户角色可访问的API资源
                                    || resourceRoleList.stream().anyMatch(resourceRole -> resourceRole.getApiResourceId().equals(resource.getId()))
            ).toList();

            // 与当前请求进行匹配，匹配成功则放行
            return allowedList.stream().anyMatch(resource ->
                    antPathMatcher.match(resource.getUrl(), url)
                            && (method.equals(resource.getMethod()) || resource.getMethod() == null)
            );
        }
    }

    /**
     * 基于API资源的鉴权策略：
     * 每次遍历所有API资源匹配当前请求，从匹配到的API资源中，找出无需登录API、仅需登录API和需要指定角色的API，与当前用户拥有的角色匹配
     */
    class ApiResourceBasedAuthorizationStrategy implements AuthorizationStrategy {

        @Override
        public boolean check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
            // 获取当前请求和url
            HttpServletRequest request = context.getRequest();
            String url = request.getRequestURI();
            String method = request.getMethod();

            // 筛选出与请求匹配的api资源，需要url匹配，并且请求方法相同或者api资源的请求方法为null
            List<SysApiResource> apiResources = apiResourceService.list().stream()
                    .filter(
                            resource ->
                                    antPathMatcher.match(resource.getUrl(), url)
                                            && (method.equals(resource.getMethod()) || resource.getMethod() == null)
                    )
                    .toList();

            // 匹配不到说明404，直接返回true
            if (apiResources.isEmpty()) {
                return true;
            }

            // 无需登录即可访问的列表
            List<SysApiResource> whiteList = apiResources.stream().filter(resource -> !resource.getRequireLogin()).toList();
            if (!whiteList.isEmpty()) {
                return true;
            }

            // 以下都是需要登录才能访问的资源，如果没有登录直接拒绝
            if (authentication.get() instanceof AnonymousAuthenticationToken) {
                return false;
            }

            // 获取当前登录用户以及其角色ID列表
            UserDetailsImpl userDetails = (UserDetailsImpl) authentication.get().getPrincipal();
            SysUser current = userDetails.getUser();
            List<Long> userRoleIds = userDetails.getRoles().stream().map(SysRole::getId).toList();

            // 超级管理员直接返回true
            if (current.getType() == 0) {
                return true;
            }

            // 只需要登录即可访问的列表
            List<SysApiResource> onlyRequireLoginList = apiResources.stream()
                    .filter(resource -> resource.getRequireLogin() && !resource.getRequirePermissions())
                    .toList();

            if (!onlyRequireLoginList.isEmpty()) {
                return true;
            }

            // 需要指定角色访问的列表
            List<SysApiResource> requirePermissionsList = apiResources.stream()
                    .filter(resource -> resource.getRequireLogin() && resource.getRequirePermissions())
                    .toList();

            List<Long> requirePermissionsIdList = requirePermissionsList.stream().map(SysApiResource::getId).toList();
            // 可访问的角色ID列表
            List<Long> requireRoleIds = apiResourceService.findApiResourceRolesByApiResourceIdIn(requirePermissionsIdList).stream()
                    .map(SysApiResourceRole::getRoleId)
                    .toList();

            // 所需角色和用户拥有角色取交集
            List<Long> matchedRoleIds = requireRoleIds.stream()
                    .filter(roleId1 -> userRoleIds.stream().anyMatch(roleId1::equals))
                    .toList();

            return !matchedRoleIds.isEmpty();
        }
    }
}